<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>基础指南 :: 梅尔沃放置指南</title>
    <meta name="generator" content="Antora 3.1.4">
    <link rel="stylesheet" href="../../../_/css/site.css">
  </head>
  <body class="article">
<header class="header">
  <nav class="navbar">
    <div class="navbar-brand">
      <a class="navbar-item" href="../../..">梅尔沃放置指南</a>
      <button class="navbar-burger" data-target="topbar-nav">
        <span></span>
        <span></span>
        <span></span>
      </button>
    </div>
    <div id="topbar-nav" class="navbar-menu">
      <div class="navbar-end">
        <a class="navbar-item" href="#">Home</a>
        <div class="navbar-item has-dropdown is-hoverable">
          <a class="navbar-link" href="#">Products</a>
          <div class="navbar-dropdown">
            <a class="navbar-item" href="#">Product A</a>
            <a class="navbar-item" href="#">Product B</a>
            <a class="navbar-item" href="#">Product C</a>
          </div>
        </div>
        <div class="navbar-item has-dropdown is-hoverable">
          <a class="navbar-link" href="#">Services</a>
          <div class="navbar-dropdown">
            <a class="navbar-item" href="#">Service A</a>
            <a class="navbar-item" href="#">Service B</a>
            <a class="navbar-item" href="#">Service C</a>
          </div>
        </div>
        <div class="navbar-item">
          <span class="control">
            <a class="button is-primary" href="#">Download</a>
          </span>
        </div>
      </div>
    </div>
  </nav>
</header>
<div class="body">
<div class="nav-container" data-component="melvor" data-version="1.0.0">
  <aside class="nav">
    <div class="panels">
<div class="nav-panel-menu is-active" data-panel="menu">
  <nav class="nav-menu">
    <h3 class="title"><a href="../index.html">梅尔沃放置</a></h3>
<ul class="nav-list">
  <li class="nav-item" data-depth="0">
<ul class="nav-list">
  <li class="nav-item" data-depth="1">
    <button class="nav-item-toggle"></button>
    <a class="nav-link" href="../战斗/index.html">战斗(no)</a>
<ul class="nav-list">
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../战斗/攻击.html">攻击(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../战斗/力量.html">力量(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../战斗/防御.html">防御(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../战斗/生命值.html">生命值(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../战斗/远程.html">远程(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../战斗/魔法.html">魔法(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../战斗/祝祭.html">祝祭(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../战斗/屠杀者.html">屠杀者(no)</a>
  </li>
</ul>
  </li>
  <li class="nav-item" data-depth="1">
    <button class="nav-item-toggle"></button>
    <span class="nav-text">非战斗(ing)</span>
<ul class="nav-list">
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../非战斗/伐木.html">伐木(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../非战斗/钓鱼.html">钓鱼(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../非战斗/生火.html">生火(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../非战斗/烹饪.html">烹饪(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../非战斗/采矿.html">采矿(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../非战斗/锻造.html">锻造(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../非战斗/扒窃.html">扒窃(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../非战斗/弓匠.html">弓匠(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../非战斗/制造.html">制造(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../非战斗/符文锻造.html">符文锻造(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../非战斗/草药学.html">草药学(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../非战斗/灵巧.html">灵巧(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../非战斗/召唤.html">召唤(ing)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../非战斗/星象学.html">星象学(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../非战斗/生活类魔法.html">生活类魔法(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../非战斗/制图学.html">制图学(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../非战斗/考古学.html">考古学(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../非战斗/农务.html">农务(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../非战斗/城镇.html">城镇(no)</a>
  </li>
</ul>
  </li>
  <li class="nav-item" data-depth="1">
    <button class="nav-item-toggle"></button>
    <span class="nav-text">练级(no)</span>
<ul class="nav-list">
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../练级/伐木.html">伐木(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../练级/钓鱼.html">钓鱼(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../练级/生火.html">生火(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../练级/烹饪.html">烹饪(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../练级/采矿.html">采矿(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../练级/锻造.html">锻造(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../练级/扒窃.html">扒窃(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../练级/弓匠.html">弓匠(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../练级/制造.html">制造(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../练级/符文锻造.html">符文锻造(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../练级/草药学.html">草药学(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../练级/灵巧.html">灵巧(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../练级/召唤-练级.html">召唤(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../练级/星象学.html">星象学(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../练级/生活类魔法.html">生活类魔法(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../练级/制图学.html">制图学(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../练级/考古学.html">考古学(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../练级/农务.html">农务(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../练级/城镇.html">城镇(no)</a>
  </li>
</ul>
  </li>
  <li class="nav-item" data-depth="1">
    <button class="nav-item-toggle"></button>
    <span class="nav-text">其它(no)</span>
<ul class="nav-list">
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../其它/新手指南.html">新手指南(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../其它/指南.html">指南(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../其它/仓库.html">仓库(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../其它/战斗.html">战斗(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../其它/专精.html">专精(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../其它/赚钱.html">赚钱(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../其它/商店.html">商店(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../其它/菜单.html">菜单(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../其它/宠物.html">宠物(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../其它/哥布林突袭.html">哥布林突袭(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../其它/版本信息.html">版本信息(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../其它/使徒的王座DLC.html">使徒的王座DLC(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../其它/探索地图集DLC.html">探索地图集DLC(no)</a>
  </li>
</ul>
  </li>
  <li class="nav-item" data-depth="1">
    <button class="nav-item-toggle"></button>
    <span class="nav-text">参考表格(no)</span>
<ul class="nav-list">
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../参考表格/道具.html">道具(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../参考表格/装备.html">装备(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../参考表格/经验表格.html">经验表格(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../参考表格/道具升级.html">道具升级(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../参考表格/战斗区域.html">战斗区域(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../参考表格/屠杀者区域.html">屠杀者区域(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../参考表格/副本.html">副本(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../参考表格/宝箱战利品表格.html">宝箱战利品表格(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../参考表格/怪物.html">怪物(no)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../参考表格/怪物掉落表格.html">怪物掉落表格(no)</a>
  </li>
</ul>
  </li>
  <li class="nav-item" data-depth="1">
    <button class="nav-item-toggle"></button>
    <span class="nav-text">指南(ing)</span>
<ul class="nav-list">
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../指南/战斗-练级.html">战斗-练级(ing)</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../指南/熔岩洞窟-指南.html">熔岩洞窟-指南</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="../指南/冒险模式-指南.html">冒险模式-指南</a>
  </li>
</ul>
  </li>
  <li class="nav-item" data-depth="1">
    <button class="nav-item-toggle"></button>
    <a class="nav-link" href="index.html">Mod开发</a>
<ul class="nav-list">
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="入门指南.html">入门指南</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="创作工具包.html">创作工具包</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="从脚本和扩展迁移.html">从脚本和扩展迁移</a>
  </li>
  <li class="nav-item is-current-page" data-depth="2">
    <a class="nav-link" href="基础指南.html">基础指南</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="Mod上下文API参考.html">Mod上下文API参考</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="侧边栏API参考.html">侧边栏API参考</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="使用PetiteVue创建可复用组件.html">使用PetiteVue创建可复用组件</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="启用Steam客户端的DevTools.html">启用Steam客户端的DevTools</a>
  </li>
  <li class="nav-item" data-depth="2">
    <a class="nav-link" href="游戏内功能参考.html">游戏内功能参考</a>
  </li>
</ul>
  </li>
  <li class="nav-item" data-depth="1">
    <button class="nav-item-toggle"></button>
    <span class="nav-text">工具</span>
<ul class="nav-list">
  <li class="nav-item" data-depth="2">
    <span class="nav-text"><a href="https://consolelog.gitee.io/caniidle/?language=%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87" target="_blank" rel="noopener">我能挂机吗</a></span>
  </li>
</ul>
  </li>
</ul>
  </li>
</ul>
  </nav>
</div>
<div class="nav-panel-explore" data-panel="explore">
  <div class="context">
    <span class="title">梅尔沃放置</span>
    <span class="version">1.0.0</span>
  </div>
  <ul class="components">
    <li class="component is-current">
      <a class="title" href="../index.html">梅尔沃放置</a>
      <ul class="versions">
        <li class="version is-current is-latest">
          <a href="../index.html">1.0.0</a>
        </li>
      </ul>
    </li>
  </ul>
</div>
    </div>
  </aside>
</div>
<main class="article">
<div class="toolbar" role="navigation">
<button class="nav-toggle"></button>
  <a href="../index.html" class="home-link"></a>
<nav class="breadcrumbs" aria-label="breadcrumbs">
  <ul>
    <li><a href="../index.html">梅尔沃放置</a></li>
    <li><a href="index.html">Mod开发</a></li>
    <li><a href="基础指南.html">基础指南</a></li>
  </ul>
</nav>
<div class="edit-this-page"><a href="file:///D:/src/game/melvor_idle_wiki/melvor/modules/ROOT/pages/mod/基础指南.adoc">Edit this Page</a></div>
</div>
  <div class="content">
<aside class="toc sidebar" data-title="目录" data-levels="3">
  <div class="toc-menu"></div>
</aside>
<article class="doc">
<h1 class="page">基础指南</h1>
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://wiki.melvoridle.com/w/Mod_Creation/Essentials" target="_target">原文地址</a></p>
</div>
<div class="paragraph">
<p>第一次为梅尔沃放置编写mod？请考虑从 <a href="入门指南.html" class="xref page">Mod开发/入门指南</a>。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_创建mod"><a class="anchor" href="#_创建mod"></a>1. 创建Mod</h2>
<div class="sectionbody">
<div class="paragraph">
<p>在梅尔沃放置中，一个mod由两部分组成：元数据和资源。</p>
</div>
<div class="paragraph">
<p><strong>元数据</strong>在你的mod的mod.io配置页面中定义（名称、版本、预览等），并在<strong>必须</strong> <em>位于你的mod目录根目录</em>下的manifest.json文件中定义（这个文件包含元数据，告诉Mod Manager如何加载你的mod）。</p>
</div>
<div class="paragraph">
<p><strong>Resources</strong> 则包含其他所有内容，如JavaScript模块和脚本、CSS样式表、图像、声音等。这些都是资源，可以通过manifest或者你的JavaScript代码动态访问。当从任何地方（manifest或代码）引用一个资源时，路径应该<strong><em>总是</em></strong>相对于你的mod的根目录（<code>manifest.json</code>文件所在的位置）。</p>
</div>
<div class="sect2">
<h3 id="_manifest"><a class="anchor" href="#_manifest"></a>1.1. Manifest</h3>
<div class="paragraph">
<p>你开始编写代码之前，一个好的做法是在manifest.json文件中定义一些元数据。一个完整的manifest.json看起来可能像下面这样：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-json hljs" data-lang="json">{
  "namespace": "helloWorld",
  "icon": "assets/icon.png",
  "setup": "src/setup.mjs",
  "load": ["assets/style.css"]
}</code></pre>
</div>
</div>
<div class="sect3">
<h4 id="_namespace_string"><a class="anchor" href="#_namespace_string"></a>1.1.1. namespace?: string</h4>
<div class="paragraph">
<p>一些重要的JavaScript modding API（工具）需要定义一个命名空间。这可以帮助游戏保持你的mod的数据有组织-你可以把它想象成你的mod的id，其他mod和游戏本身将使用这个id来访问你定义存储的数据。因此，最好选择一个容易识别你的mod的命名空间，以备其他mod与其交互。</p>
</div>
<div class="paragraph">
<p>命名空间只能包含字母数字字符和下划线，不能以“melvor”开头。</p>
</div>
<table class="tableblock frame-all grid-all fit-content">
<colgroup>
<col>
<col>
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-center valign-middle"><p class="tableblock">Namespace</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Valid</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>helloWorld</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔️</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>hello_world_123</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔️</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>HelloWorld!</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">❌</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>melvorWorld</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">❌</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>尽管这个属性是可选的，但包含它是一个好习惯，以避免将来如果你最终使用一个需要命名空间的API时需要进行故障排除。</p>
</div>
</div>
<div class="sect3">
<h4 id="_icon_string"><a class="anchor" href="#_icon_string"></a>1.1.2. icon?: string</h4>
<div class="paragraph">
<p>在Mod管理器中的“我的Mods”列表等地方与您的Mod一起显示的可选图标。值应该是相对于您的mod的根目录（manifest所在的位置）的图像文件路径。可接受的图标文件类型是<code>.png</code>或<code>.svg</code>，图标通常在游戏中以最大38像素显示。</p>
</div>
<div class="paragraph">
<p>是的，您也可以提供一个绝对路径到网络URL，这将会被使用代替之前的图标路径。</p>
</div>
</div>
<div class="sect3">
<h4 id="_setup_string"><a class="anchor" href="#_setup_string"></a>1.1.3. setup?: string</h4>
<div class="paragraph">
<p>这个属性仅在清单中不存在<code>“load”</code>属性时才<strong>需要</strong>。</p>
</div>
<div class="paragraph">
<p>这个值应该是一个指向作为你的mod入口点的JavaScript模块的路径；这个概念将在后面的部分进一步介绍。</p>
</div>
</div>
<div class="sect3">
<h4 id="_load_string_string"><a class="anchor" href="#_load_string_string"></a>1.1.4. load?: string | string[]</h4>
<div class="paragraph">
<p>这个属性仅在清单中不存在<code>“setup”</code>属性时才<strong>需要</strong>。</p>
</div>
<div class="paragraph">
<p>此属性的值可以是单一路径，也可以是需加载资源的路径数组。这些资源在<code>“setup”</code>资源运行后，按照数组的顺序加载。可通过此属性加载的资源包括JavaScript脚本文件（<code>.js</code>）、JavaScript模块文件（<code>.mjs</code>）、CSS样式表（<code>.css</code>）、包含游戏数据包的JSON文件（<code>.json</code>）以及包含模板的HTML文件（<code>.html</code>）。然而，除非您的mod非常简单，否则建议通过代码在mod的入口点（<code>“setup”</code>）加载JavaScript资源（<code>.js</code>或<code>.mjs</code>）。</p>
</div>
<div class="paragraph">
<p>同样重要的是要注意，虽然<code>.js</code>被认为是“setup”属性中JavaScript模块文件的合法扩展名，但通过“load”加载的模块必须以<code>.mjs</code>结尾，否则将被视为普通脚本文件。</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_结构化你的代码"><a class="anchor" href="#_结构化你的代码"></a>1.2. 结构化你的代码</h3>
<div class="sect3">
<h4 id="_使用模块推荐"><a class="anchor" href="#_使用模块推荐"></a>1.2.1. 使用模块（推荐）</h4>
<div class="paragraph">
<p>可以将代码的结构组织为脚本或模块、<code>“setup”</code>或<code>“load”</code>等许多方式。每种方式都可能有其优点，但对于大多数mod来说，推荐的方法是使用 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules">JavaScript模块</a> 编写代码，并具有一个单一的入口点（在manifest.json中定义为“setup”），同时将“load”属性专门用于加载CSS。</p>
</div>
<div class="paragraph">
<p>使用这种方法可以使您的代码保持清晰和易于管理，同时 <a href="https://www.tutorialspoint.com/what-is-global-namespace-pollution-in-javascript">避免污染全局JavaScript范围</a>，因此可以避免与其他mod发生冲突。如果您不熟悉JavaScript模块，可以查看 <a href="https://www.w3schools.com/js/js_modules.asp">W3Schools的JavaScript Modules</a> 或 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules">MDN</a> 上的资源以了解更详细的信息。您将使用的通用模式是导出模块“features”（函数、变量等），然后将其导入其他模块以供使用。这也是本指南在其所有示例中使用的做法。</p>
</div>
<div class="paragraph">
<p>让我们从您的mod的<code>“setup”</code>入口点定义的模块开始：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export function setup(ctx) {
  console.log('Hello World!');
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>我们在这里导出一个名为<code>setup</code>的函数，因为这是Mod Manager在加载<code>“setup”</code>模块时所寻找的。如果没有这个函数，加载此mod时会抛出错误。在mod加载后，此<code>setup</code>函数被调用并接收mod的上下文对象，这发生在角色选择屏幕可见之前。因此，此mod会在此时将&#8217;Hello World!'写入控制台。</p>
</div>
<div class="paragraph">
<p>然而，为了充分利用JavaScript的模块化方法，我们不能像在常规JavaScript环境中那样在文件的顶部使用静态导入。由于modding资源的存储方式，我们必须使用特殊的<code>loadModule</code>函数来动态地导入模块。这与 <a href="https://www.w3docs.com/learn-javascript/dynamic-imports.html">动态导入</a> 完全相同，但它允许您使用相对于mod根目录的资源路径。</p>
</div>
<div class="paragraph">
<p>如果我们定义了一个helper模块helper.mjs：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// helper.mjs
export function greet(name) {
  console.log(`Hello, ${name}!`);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>当然，我们可以使用在我们的setup函数中导出的代码:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export async function setup({ loadModule }) {
  const { greet } = await loadModule('helper.mjs');
  greet('Melvor'); // &gt; Hello, Melvor!
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>如果你需要从你的助手模块中访问上下文对象，有两种方法：</p>
</div>
<div class="paragraph">
<p>1.从设置函数中将上下文对象传递给已加载的模块：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// configService.mjs
export function init(ctx) {
  // Perform actions using the context object here...
}

// setup.mjs
export async function setup(ctx) {
  const configService = await ctx.loadModule('configService.mjs');
  configService.init(ctx);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>2.在全局<code>mod</code>对象上使用<code>getContext</code>方法：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// configService.mjs
const ctx = mod.getContext(import.meta);

export function init() {
  // Perform actions using the context object here...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>你必须将<code>import.meta</code>传递给<code>mod.getContext</code>方法，这是一个在所有模块中可用的特殊JavaScript对象，以接收你的mod的上下文对象。</p>
</div>
</div>
<div class="sect3">
<h4 id="_使用脚本"><a class="anchor" href="#_使用脚本"></a>1.2.2. 使用脚本</h4>
<div class="paragraph">
<p>如果你选择在你的mod中包含普通脚本，无论是出于熟悉还是特殊的使用情况，你都可以通过上下文对象（可能是从<code>“setup”</code>模块接收的）或manifest的<code>“load”</code>属性将脚本加载（注入）到游戏中。</p>
</div>
<div class="paragraph">
<p>通过上下文对象加载脚本与加载模块非常相似，但你不会得到返回值。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">export async function setup({ loadScript }) {
  // Make sure you await the call to loadScript if your code beyond relies on it
  await loadScript('hello-melvor-script.js');
  // hello-melvor-script.js has executed

  // But don't bother awaiting it if it's not time-sensitive
  loadScript('some-independent-script.js');
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>从你的脚本内部，你仍然可以访问上下文对象：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">mod.register(ctx =&gt; {
  // Use the context object here
});</code></pre>
</div>
</div>
<div class="paragraph">
<p>请注意，mod.register方法只适用于通过loadScript或manifest的“load”属性注入的脚本。</p>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_上下文对象"><a class="anchor" href="#_上下文对象"></a>2. 上下文对象</h2>
<div class="sectionbody">
<div class="paragraph">
<p>你的mod的上下文对象是用于安装你的mod并对游戏进行修改的中心点。本指南的大多数其他部分将介绍通过该对象上可用的API实现的概念。要深入了解上下文对象的文档，请参阅 <a href="Mod上下文API参考.html" class="xref page">Mod开发/Mod上下文API参考</a>。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_访问mod的资源"><a class="anchor" href="#_访问mod的资源"></a>3. 访问Mod的资源</h2>
<div class="sectionbody">
<div class="paragraph">
<p>请在此处查看与此主题相关的API参考文档 <a href="Mod上下文API参考.html#加载资源" class="xref page">Mod开发/Mod上下文API参考#加载资源</a>。</p>
</div>
<div class="paragraph">
<p>很可能你将打包一些在你的manifest中没有定义加载选项的资源在你的mod中，而是需要通过你的代码来加载这些资源。你的mod的上下文对象提供了检索这些资源的方法。请记住，所有指向你的资源的文件路径应该是<em>相对于你的mod的根目录的</em>。下面是一些常见的场景：</p>
</div>
<div class="sect2">
<h3 id="_加载导入模块"><a class="anchor" href="#_加载导入模块"></a>3.1. 加载（导入）模块</h3>
<div class="paragraph">
<p>使用<code>ctx.loadModule</code>导入JavaScript模块导出的特性。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// my-module.mjs
export function greet(name) {
  console.log(`Hello, ${name}!`);
}

export const importantData = ['e', 'r', 'e', 'h', 't', ' ', 'o', 'l', 'l', 'e', 'h'];</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export async function setup({ loadModule }) {
  const myModule = await loadModule('my-module.mjs');
  myModule.greet('Melvor'); // Hello, Melvor!
  console.log(myModule.importantData.reverse().join('')); // hello there
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_加载注入脚本"><a class="anchor" href="#_加载注入脚本"></a>3.2. 加载（注入）脚本</h3>
<div class="paragraph">
<p>使用<code>ctx.loadModule</code>导入JavaScript模块导出的特性。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export await function setup({ loadScript }) {
  // Wait for script to run
  await loadScript('my-script.js');
  // Or not
  loadScript('my-independent-script.js');
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_加载注入html模板"><a class="anchor" href="#_加载注入html模板"></a>3.3. 加载（注入）HTML模板</h3>
<div class="paragraph">
<p>使用<code>ctx.loadTemplates</code>将所有<code>&lt;template&gt;</code>元素注入到文档主体中。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export function setup({ loadTemplates }) {
  loadTemplates('my-templates.html');
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_加载注入样式表"><a class="anchor" href="#_加载注入样式表"></a>3.4. 加载（注入）样式表</h3>
<div class="paragraph">
<p>使用<code>ctx.loadStylesheet</code>将CSS文件注入到页面中。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export function setup({ loadStylesheet }) {
  loadStylesheet('my-styles.css');
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_从json加载数据"><a class="anchor" href="#_从json加载数据"></a>3.5. 从JSON加载数据</h3>
<div class="paragraph">
<p>使用<code>ctx.loadData</code>读取并自动解析JSON资源。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// my-data.json
{
  "coolThings": [
    "rocks"
  ]
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><em>JSON中的注释</em>纯粹是说明性的，不是有效的标记</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export async function setup({ loadData }) {
  const data = await loadData('my-data.json');
  console.log(data.coolThings[0]); // ['rocks']
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_图像声音和其他内容"><a class="anchor" href="#_图像声音和其他内容"></a>3.6. 图像、声音和其他内容</h3>
<div class="paragraph">
<p>几乎可以使用ctx.getResourceUrl访问任何资源并以某种方式使用它上面提到的助手方法在幕后都使用这个方法。有了资源的URL，您可以使用内置的JavaScript方法来使用资源。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export function setup({ getResourceUrl }) {
  const url = getResourceUrl('sea-shanty-2.ogg');
  const song = new Audio(url);
  song.loop = true;
  song.play();
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_游戏生命周期钩子"><a class="anchor" href="#_游戏生命周期钩子"></a>4. 游戏生命周期钩子</h2>
<div class="sectionbody">
<div class="paragraph">
<p>请在此处查看与此主题相关的API参考文档 <a href="Mod上下文API参考.html#生命周期钩子" class="xref page">Mod开发/Mod上下文API参考#生命周期钩子</a>。</p>
</div>
<div class="paragraph">
<p>利用游戏的生命周期钩子可以让你的mod在特定时间执行操作，这对于等待某些游戏对象可用可能很有用。游戏的生命周期钩子如下：</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>onModsLoaded</code>：在所有启用的mod完成初始加载后发生。</p>
</li>
<li>
<p><code>onCharacterSelectionLoaded</code>：在角色选择屏幕完全加载后发生。</p>
</li>
<li>
<p><code>onCharacterLoaded</code>：在选择了角色并且所有游戏对象都已构造之后，但在计算离线进度之前发生。</p>
</li>
<li>
<p><code>onInterfaceReady</code>：在计算离线进度之后并且游戏界面可以可靠地修改时发生。对于任何可能通过增加角色加载时间对玩家体验产生负面影响的长运行或非关键性进程也很有用。</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>所有游戏的生命周期钩子都可通过mod的上下文对象获得，并且接受一个回调函数作为唯一参数。此回调函数可以是同步的也可以是异步的，将在指定时间执行并接收mod的上下文对象作为参数。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export function setup({ onModsLoaded, onCharacterLoaded, onInterfaceReady }) {
  onModsLoaded(ctx =&gt; {
    // Utilize other mod APIs at character select
  });

  onCharacterSelectionLoaded(ctx =&gt; {
    // Build or modify character selection UI elements
  });

  onCharacterLoaded(ctx =&gt; {
    // Modify or hook into game objects to influence offline calculations
  });

  onInterfaceReady(ctx =&gt; {
    // Build or modify in-game UI elements
  });
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_添加和修改游戏对象"><a class="anchor" href="#_添加和修改游戏对象"></a>5. 添加和修改游戏对象</h2>
<div class="sectionbody">
<div class="paragraph">
<p>请在此处查看与此主题相关的API参考文档 <a href="Mod上下文API参考.html#注册游戏对象" class="xref page">mod/Mod上下文API参考.adoc#注册游戏对象</a>。</p>
</div>
<div class="paragraph">
<p>mod现在可以以精简的方式注册或修改游戏对象（物品、技能、页面等）。这样做的入口点或者是<code>manifest.json</code>的<code>“load”</code>属性，或者是mod context API中的<code>gameData</code>端点。不同游戏对象类型所需的数据种类各不相同，但一般概念是相同的。您需要定义一个使用JSON的数据包并将其加载到游戏中，或者您可以通过代码动态地构建一个（某些动态对象，例如技能，需要后者）。</p>
</div>
<div class="sect2">
<h3 id="_定义数据包"><a class="anchor" href="#_定义数据包"></a>5.1. 定义数据包</h3>
<div class="paragraph">
<p>构建游戏对象数据的第一个（也是更简单的）选项是在一个<code>.json</code>文件中定义所有的（或尽可能多的）数据，然后将其读取到游戏中以注册您的游戏对象。</p>
</div>
<div class="paragraph">
<p>优点</p>
</div>
<div class="ulist">
<ul>
<li>
<p>更简单</p>
</li>
<li>
<p>更容易将数据与mod的逻辑分开</p>
</li>
<li>
<p>您的文本编辑器可以通过在JSON文件中定义$schema属性来提供键入功能</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>缺点</p>
</div>
<div class="ulist">
<ul>
<li>
<p>不支持所有游戏对象类型，例如技能</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>要开始使用这种方法，您的JSON文件应全部使用以下内容构建：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-json hljs" data-lang="json">{
  "$schema": "https://melvoridle.com/assets/schema/gameData.json",
  "data": {

  }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>如果你正在使用支持它的文本编辑器，你现在应该可以在你创建的字段上获得自动完成和类型检查功能。</p>
</div>
<div class="paragraph">
<p>以下是一个定义物品的示例：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-json hljs" data-lang="json">{
  "$schema": "https://melvoridle.com/assets/schema/gameData.json",
  "data": {
    "items": [{
      "id": "Wooden_Dagger",
      "name": "Wooden Dagger",
      "category": "Combat",
      "type": "Weapon",
      "media": "wooden-dagger.png",
      "ignoreCompletion": false,
      "obtainFromItemLog": false,
      "golbinRaidExclusive": false,
      "sellsFor": 0,
      "tier": "wooden",
      "validSlots": ["Weapon"],
      "occupiesSlots": [],
      "equipRequirements": [
        {
          "type": "SkillLevel",
          "skillID": "melvorD:Attack",
          "level": 1
        }
      ],
      "equipmentStats": [
        { "key": "attackSpeed", "value": 2200 },
        { "key": "stabAttackBonus", "value": 4 },
        { "key": "slashAttackBonus", "value": 1 },
        { "key": "blockAttackBonus", "value": 4 },
        { "key": "meleeStrengthBonus", "value": 1 }
      ],
      "itemType": "Weapon",
      "attackType": "melee"
    }]
  }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>然后，你可以使用以下方法之一注册你的游戏数据：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-json hljs" data-lang="json">// manifest.json
{
  "namespace": "helloWorld",
  "load": ["path-to-your-data.json"]
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><em>JSON中的注释纯粹是说明性的，不是有效的标记</em></p>
</div>
<div class="paragraph">
<p><em>或者</em></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-json hljs" data-lang="json">// setup.mjs
export async function setup({ gameData }) {
  await gameData.addPackage('path-to-your-data.json');
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_在运行时构建数据包"><a class="anchor" href="#_在运行时构建数据包"></a>5.2. 在运行时构建数据包</h3>
<div class="paragraph">
<p>动态地通过mod context API构建游戏对象数据的另一种选项是：</p>
</div>
<div class="paragraph">
<p>优点</p>
</div>
<div class="ulist">
<ul>
<li>
<p>可用于注册任何类型的游戏对象</p>
</li>
<li>
<p>能够动态构建游戏对象</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>缺点</p>
</div>
<div class="ulist">
<ul>
<li>
<p>更混乱且更复杂</p>
</li>
<li>
<p>目前没有类型支持</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>使用此方法的入口点如下所示：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export function setup({ gameData }) {
  gameData.buildPackage((p) =&gt; {
    // use the `p` object to add game objects
  }).add();
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>以下面的例子为例，添加一个项目：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export function setup({ gameData }) {
  gameData.buildPackage((p) =&gt; {
    p.items.add({
      id: 'Wooden_Dagger',
      name: 'Wooden Dagger',
      category: 'Combat',
      type: 'Weapon',
      media: 'wooden-dagger.png',
      ignoreCompletion: false,
      obtainFromItemLog: false,
      golbinRaidExclusive: false,
      sellsFor: 0,
      tier: 'wooden',
      validSlots: ['Weapon'],
      occupiesSlots: [],
      equipRequirements: [{
        type: 'SkillLevel',
        skillID: 'melvorD:Attack',
        level: 1
      }],
      equipmentStats: [
        { key: 'attackSpeed', value: 2200 },
        { key: 'stabAttackBonus', value: 4 },
        { key: 'slashAttackBonus', value: 1 },
        { key: 'blockAttackBonus', value: 4 },
        { key: 'meleeStrengthBonus', value: 1 }
      ],
      itemType: 'Weapon',
      attackType: 'melee'
    });
  }).add();
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>通过调用内置包的<code>.add()</code>方法，您的游戏数据应该已经被注册了。</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_mod设置"><a class="anchor" href="#_mod设置"></a>6. Mod设置</h2>
<div class="sectionbody">
<div class="paragraph">
<p>请在此处查看与此主题相关的API参考文档 <a href="Mod上下文API参考.html#Mod配置" class="xref page">mod/Mod上下文API参考.adoc#Mod配置</a>。</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
当通过Creator Toolkit以本地mod的形式加载您的mod时，必须将mod链接到mod.io，并且您必须通过mod.io订阅并安装该mod才能使这些数据持久存在。
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>您的mod可以定义供玩家交互的设置，并在游戏中直观地配置您的mod。此功能可通过上下文对象上的<code>settings</code>属性进行访问。如果您的mod定义了任何设置，则您的mod将出现在侧边栏下的Mod Settings中。单击此选项将打开一个窗口，其中包含您定义的所有设置。</p>
</div>
<div class="paragraph">
<p>设置的值是基于每个角色的进行保存的，并保存在角色的保存文件中。</p>
</div>
<div class="paragraph">
<p>设置（在代码中和视觉上）分为多个部分。使用<code>settings</code>对象上的<code>section(name)</code>方法获取或创建部分。传递给<code>name</code>参数的值用作该部分的标题，因此应该易于人类阅读。这些部分按照创建它们的顺序显示。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export function setup({ settings }) {
  // Creates a section labeled "General"
  settings.section('General');

  // Future calls to that section will not create a new "General" section, but instead return the already existing one
  settings.section('General');
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>使用<code>section()</code>方法返回的对象可以用于为该部分添加设置。有关设置配置的详细信息，请参阅下一节。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export function setup({ settings }) {
  const generalSettings = settings.section('General');
  // You can add settings one-by-one
  generalSettings.add({
    type: 'switch',
    name: 'awesomeness-detection',
    label: 'Awesomeness Detection',
    hint: 'Determines if you are awesome or not.',
    default: false
  });

  // Or multiple at a time by passing in an array
  generalSettings.add([{
    type: 'label',
    display: 'I am just a label though my story seldom told...'
  }, {
    type: 'number',
    name: 'pick-a-number',
    label: 'Pick a Number',
    hint: '1 through 10'
  }]);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>然后，您可以通过其<code>name</code>属性<code>get</code>或<code>set</code>任何已定义设置的值。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// elsewhere.mjs
const { settings } = mod.getContext(import.meta);

const generalSettings = settings.section('General');
generalSettings.set('pick-a-number', 1);
console.log(generalSettings.get('pick-a-number')); // 1</code></pre>
</div>
</div>
<div class="sect2">
<h3 id="_设置类型"><a class="anchor" href="#_设置类型"></a>6.1. 设置类型</h3>
<div class="paragraph">
<p>目前有八种预定义的设置类型可以自动创建可用的输入：</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Text（文本）</p>
</li>
<li>
<p>Number（数字）</p>
</li>
<li>
<p>Switch（开关）</p>
</li>
<li>
<p>Dropdown（下拉列表）</p>
</li>
<li>
<p>Button（按钮）</p>
</li>
<li>
<p>Checkbox Group（复选框组）</p>
</li>
<li>
<p>Radio Group（单选按钮组）</p>
</li>
<li>
<p>Label（标签）</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>此外，您可以通过配置其他属性添加自定义设置。有关这些配置选项的更多信息，请参阅 <a href="Mod上下文API参考.html#Mod配置" class="xref page">mod/Mod上下文API参考.adoc#Mod配置</a>。</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_自定义侧边栏"><a class="anchor" href="#_自定义侧边栏"></a>7. 自定义侧边栏</h2>
<div class="sectionbody">
<div class="paragraph">
<p><em>请在此处查看与此主题相关的API参考文档 <a href="侧边栏API参考.html" class="xref page">侧边栏API参考</a>。</em></p>
</div>
<div class="paragraph">
<p>如果您想要添加或修改游戏内侧边栏（带有银行、技能等菜单），则可以使用全局范围内可用的游戏内API，即<code>sidebar</code>。侧边栏分为四个级别：</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Sidebar（侧边栏）</p>
<div class="ulist">
<ul>
<li>
<p>Categories（类别）</p>
<div class="ulist">
<ul>
<li>
<p>Items（项目）</p>
<div class="ulist">
<ul>
<li>
<p>Subitems（子项）</p>
</li>
</ul>
</div>
</li>
</ul>
</div>
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="paragraph">
<p>例如，战斗和攻击是类别中的一个项目。子项用于完成日志的部分。</p>
</div>
<div class="paragraph">
<p>每个可自定义的（类别、项目、子项）部分通常使用相同的方式进行交互。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">const combat = sidebar.catetory('Combat'); // Get the Combat category, or create one if it doesn't exist
const attack = sidebar.category('Combat').item('Attack'); // Get the Attack item within Combat or create one if it doesn't exist
attack.subitem('Wut'); // Get the Wut subitem within Attack or create one if it doesn't exist</code></pre>
</div>
</div>
<div class="paragraph">
<p>此外，可以使用配置对象作为第二个参数来调用这些部分，以使用新的配置创建或更新现有的部分。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">sidebar.category('Combat').item('Slayer', {
  before: 'Attack', // Move the Slayer item above Attack
  ignoreToggle: true // Keep Slayer visible when its category has been hidden
});</code></pre>
</div>
</div>
<div class="paragraph">
<p>每个侧边栏部分的配置对象的完整定义可以在 <a href="侧边栏API参考.html" class="xref page">侧边栏API参考</a> 中找到。</p>
</div>
<div class="paragraph">
<p>如果您需要检索所有现有的类别、项目或子项，请使用它们各自的方法：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">sidebar.categories(); // returns an array of all categories
sidebar.category('Combat').items(); // returns an array of all Combat items
sidebar.category('General').item('Completion Log').subitems(); // returns an array of all Completion Log subitems</code></pre>
</div>
</div>
<div class="paragraph">
<p>删除类别、项目和子项也是可能的：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">sidebar.category('Non-Combat').remove(); // Remove the entire Non-Combat category
sidebar.removeCategory('Combat'); // Alternative (this avoids creating a Combat category if it didn't already exist)
sidebar.removeAllCategories(); // Remove all categories, but why?

// Same kind of structure for items and subitems:
sidebar.category('Modding').item('Mod Manager').remove();
sidebar.category('General').item('Completion Log').removeAllSubitems();</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_创建可重用的html组件"><a class="anchor" href="#_创建可重用的html组件"></a>8. 创建可重用的HTML组件</h2>
<div class="sectionbody">
<div class="paragraph">
<p>这个主题在 <a href="使用PetiteVue创建可复用组件.html" class="xref page">使用PetiteVue创建可复用组件</a> 中有更详细的介绍。</p>
</div>
<div class="paragraph">
<p>梅尔沃放置随附 <a href="https://github.com/vuejs/petite-vue">PetiteVue</a>，供mod用于创建可重复使用的HTML组件。您可以使用 <a href="https://github.com/vuejs/petite-vue">官方GitHub页面</a>上的文档来协助使用PetiteVue库。但是，有一些辅助函数可以让mod更容易与之交互。</p>
</div>
<div class="sect2">
<h3 id="_导入html模板"><a class="anchor" href="#_导入html模板"></a>8.1. 导入HTML模板</h3>
<div class="paragraph">
<p>使用<code>manifest.json</code>的<code>“load”</code>属性或上下文API的<code>loadTemplates</code>方法，可以将HTML文件中的所有<code>&lt;template&gt;</code>元素导入到文档主体中。这些元素在创建组件时可供使用。</p>
</div>
<div class="paragraph">
<p>如果您有以下HTML文件：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-html hljs" data-lang="html">&lt;!-- templates.html --&gt;
&lt;template id="counter-component"&gt;
  &lt;span class="text-light"&gt;{{ count }}&lt;/span&gt;
  &lt;button class="btn btn-secondary" @click="inc"&gt;+&lt;/button&gt;
&lt;/template&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>您可以通过以下两种方式之一导入模板：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-json hljs" data-lang="json">// manifest.json
{
  "load": "templates.html"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><em>JSON中的注释纯粹是说明性的，不是有效的标记</em></p>
</div>
<div class="paragraph">
<p><em>或者</em></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export function setup({ loadTemplates }) {
  loadTemplates('templates.html');
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_定义组件"><a class="anchor" href="#_定义组件"></a>8.2. 定义组件</h3>
<div class="paragraph">
<p>根据 <a href="https://github.com/vuejs/petite-vue#components">PetiteVue文档对组件的说明</a>，您应该将每个组件定义为函数。该组件应该使用<code>$template</code>属性定义其模板选择器，然后定义渲染组件将使用的任何其他属性或方法。例如：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">function Counter(props) {
  return {
    $template: '#counter-component',
    count: props.count,
    inc() {
      this.count++;
    }
  };
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_在ui内创建组件"><a class="anchor" href="#_在ui内创建组件"></a>8.3. 在UI内创建组件</h3>
<div class="paragraph">
<p>现在，既然您的模板已加载并且您已经定义了组件，就可以使用辅助函数<code>ui.create</code>在UI中创建该组件的实例。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// Create a counter component at the bottom of the Woodcutting page
ui.create(Counter({ count: 0 }), document.getElementById('woodcutting-container'));</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_数据存储"><a class="anchor" href="#_数据存储"></a>9. 数据存储</h2>
<div class="sectionbody">
<div class="paragraph">
<p><em>请在此处查看角色存储的相关API参考文档 <a href="Mod上下文API参考.html#存储角色数据" class="xref page">mod/Mod上下文API参考.adoc#存储角色数据</a>。</em></p>
</div>
<div class="paragraph">
<p><em>请在此处查看账户存储的相关API参考文档 <a href="Mod上下文API参考.html#存储账号数据" class="xref page">mod/Mod上下文API参考.adoc#存储账号数据</a>。</em></p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
当通过Creator Toolkit以本地mod的形式加载您的mod时，必须将mod链接到mod.io，并且您必须通过mod.io订阅并安装该mod才能使这些数据持久保存。
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>为游戏或设置中已保存的数据之外的数据保存有两种选项：与角色一起保存的数据或保存到玩家帐户的数据。然而，对于大多数情况，应优先选择使用角色存储，并谨慎使用帐户存储。通过您mod的上下文对象，可以分别访问<code>characterStorage</code>和<code>accountStorage</code>，这两种存储具有相同的方法和行为。角色存储在加载角色后才可用（<code>onCharacterLoaded</code>生命周期钩子）。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export function setup({ characterStorage }) {
  // This would all function identically with accountStorage, but also be available across characters
  characterStorage.setItem('my-favorite-pet', 7);

  console.log(PETS[characterStorage.getItem('my-favorite-pet')].name); // Larry, the Lonely Lizard

  characterStorage.removeItem('my-favorite-pet');

  characterStorage.clear(); // Removes all currently stored items
}</code></pre>
</div>
</div>
<div class="sect2">
<h3 id="_限制"><a class="anchor" href="#_限制"></a>9.1. 限制</h3>
<div class="paragraph">
<p>目前，mod的角色存储和帐户存储各自（单独地）限制为总共8，192字节（8kb）的数据。这意味着每个角色可以每个mod存储最多8kb的数据，但帐户只能存储总共8kb的数据。</p>
</div>
<div class="paragraph">
<p>此外，只能存储JSON可序列化的数据。这包括任何JavaScript原始值（字符串、数字和布尔值）或仅包含原始值（或仅包含原始值的对象或数组等）的对象或数组。您不需要自己序列化/反序列化数据。</p>
</div>
<div class="paragraph">
<p>最后，由于帐户数据被持久保存在云中，因此由于玩家可能遇到的网络问题，数据完整性不能得到100％保证。</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_游戏对象修补钩子"><a class="anchor" href="#_游戏对象修补钩子"></a>10. 游戏对象修补/钩子</h2>
<div class="sectionbody">
<div class="paragraph">
<p>请在此处查看与此主题相关的API参考文档 <a href="Mod上下文API参考.html#游戏对象修补-钩子" class="xref page">mod/Mod上下文API参考.adoc#游戏对象修补-钩子</a>。</p>
</div>
<div class="paragraph">
<p>常见的一种modding方案是想要覆盖/修改游戏中的某个方法，或者在它完成之前或之后执行某个操作。您的mod的上下文对象包含一个patch属性，可以用于这种情况。补丁只能应用于JavaScript类（<code>Player</code>、<code>Enemy</code>、<code>CombatManager</code>、<code>Woodcutting</code>等）中存在的类和方法。要开始，请定义您想要修补的类和方法：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export function setup({ patch }) {
  const xpPatch = patch(Skill, 'addXP');
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>从那里，您可以使用该补丁执行以下任何操作。</p>
</div>
<div class="sect2">
<h3 id="_在之前执行某项操作"><a class="anchor" href="#_在之前执行某项操作"></a>10.1. 在之前执行某项操作</h3>
<div class="paragraph">
<p>在补丁对象上使用before方法，以在补丁方法之前立即执行代码。此外，回调钩子将接收用于调用补丁方法的参数作为参数，并且可以选择通过返回新参数作为数组来修改它们。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export function setup({ patch }) {
  patch(Skill, 'addXP').before((amount, masteryAction) =&gt; {
    console.log(`Doubling XP from ${amount} to ${amount * 2}!`);
    return [amount * 2, masteryAction]; // Double all XP gains
  });
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_在之后执行某项操作"><a class="anchor" href="#_在之后执行某项操作"></a>10.2. 在之后执行某项操作</h3>
<div class="paragraph">
<p>在补丁对象上使用<code>after</code>方法，以在补丁方法之后立即执行代码。此外，回调钩子将接收从补丁方法返回的值以及用于调用它的参数作为参数。另外，after钩子可以选择通过返回一个值本身来覆盖返回值。<em><strong>只有</strong>返回值为<code>undefined</code>才会被忽略</em>。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export function setup({ patch }) {
  patch(Player, 'rollToHit').after((willHit) =&gt; {
    if (!willHit) console.log('A miss? I think not!');
    return true;
  });
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_完全替换方法"><a class="anchor" href="#_完全替换方法"></a>10.3. 完全替换方法</h3>
<div class="paragraph">
<p>补丁对象上的<code>replace</code>方法将覆盖补丁方法的主体，但before和after钩子仍将被执行。替换方法将接收当前方法的实现（正在被替换的那个）以及用于调用它的参数作为参数。替换方法的返回值将是方法调用的返回值，可能会受到after钩子中进行的任何更改的影响。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export function setup({ patch }) {
  patch(Skill, 'addXP').replace(function(o, amount, masteryAction) {
    // Prevent any woodcutting XP
    if (this.id === 'melvorD:Woodcutting') return;

    // Double any mining XP
    if (this.id === 'melvorD:Mining') return o(amount * 2, masteryAction);

    // Grant all other XP as normal
    return o(amount, masteryAction);
  });
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>需要注意的是，使用replace方法会替换<strong>当前</strong>方法的实现。这意味着，对同一个补丁方法执行多个替换将按照与它们声明的相反顺序执行：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export function setup({ patch, onInterfaceReady }) {
  const xpPatch = patch(Skill, 'addXP');

  xpPatch.replace(xpA);
  xpPatch.replace(xpB);

  onInterfaceReady(() =&gt; {
    game.woodcutting.addXP(100);
    // Logs:
    // XP replace B
    // XP replace A
  });
}

function xpA(o, amount, masteryAction) {
  console.log('XP replace A');
  return o(amount, masteryAction);
}

function xpB(o, amount, masteryAction) {
  console.log('XP replace B');
  return o(amount, masteryAction);
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_暴露api"><a class="anchor" href="#_暴露api"></a>11. 暴露API</h2>
<div class="sectionbody">
<div class="paragraph">
<p>请在此处查看与此主题相关的API参考文档 <a href="Mod上下文API参考.html#公开属性与方法" class="xref page">Mod开发/Mod上下文API参考(Mod API)</a>。</p>
</div>
<div class="paragraph">
<p>如果您的mod充当其他mod集成使用的工具，则使用<code>ctx.api</code>通过上下文对象公开API是推荐的做法。这与使用模块开发的mod一起使用时特别有用。<code>api</code>方法接受一个对象，并将该对象上的任何属性公开到<code>api['your-mods-namespace']</code>属性中的全局<code>mod</code>对象上。您可以多次调用<code>api</code>方法来添加更多API。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-json hljs" data-lang="json">// manifest.json
{
  "namespace": "helloWorld",
  "setup": "setup.mjs"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><em>JSON中的注释纯粹是说明性的，不是有效的标记</em></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// setup.mjs
export function setup({ api }) {
  api({
    greet: name =&gt; console.log(`Hello, ${name!}`);
  });
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>其他mod将能够与您的API进行交互：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// some other mod
mod.api.helloWorld.greet('Melvor'); // Hello, Melvor!</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_dev上下文"><a class="anchor" href="#_dev上下文"></a>12. Dev上下文</h2>
<div class="sectionbody">
<div class="paragraph">
<p>为了在上传mod之前更容易地测试代码，存在一个&#8217;dev' mod上下文，您可以访问它以尝试使用上下文对象中不需要额外资源的任何方法，即您不能使用<code>loadModule</code>。要访问此上下文，您可以在浏览器控制台中使用以下内容：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">const devCtx = mod.getDevContext();</code></pre>
</div>
</div>
<div class="paragraph">
<p><em><strong>不应</strong>在mod内部使用此方法/上下文。</em></p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_下一步"><a class="anchor" href="#_下一步"></a>13. 下一步</h2>
<div class="sectionbody">
<div class="paragraph">
<p>希望本指南涵盖了大多数常见的modding场景，并将在您mod开发过程中成为有用的参考。要深入了解特定概念，请考虑查看以下指南：</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="Mod上下文API参考.html" class="xref page">Mod上下文API参考</a></p>
</li>
<li>
<p><a href="侧边栏API参考.html" class="xref page">侧边栏API参考</a></p>
</li>
</ul>
</div>
<table class="tableblock frame-all grid-all fit-content">
<colgroup>
<col>
<col>
<col>
<col>
<col>
<col>
<col>
<col>
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-center valign-middle" colspan="8"><p class="tableblock"><strong>Mod指南</strong></p></td>
</tr>
<tr>
<td class="tableblock halign-center valign-middle"><p class="tableblock"><a href="入门指南.html" class="xref page">Mod开发的入门指南</a></p></td>
<td class="tableblock halign-center valign-middle"><p class="tableblock"><a href="创作工具包.html" class="xref page">创作工具包</a></p></td>
<td class="tableblock halign-center valign-middle"><p class="tableblock"><a href="从脚本和扩展迁移.html" class="xref page">从脚本和扩展迁移</a></p></td>
<td class="tableblock halign-center valign-middle"><p class="tableblock"><a href="基础指南.html" class="xref page">基础指南</a></p></td>
<td class="tableblock halign-center valign-middle"><p class="tableblock"><a href="Mod上下文API参考.html" class="xref page">Mod上下文API参考</a></p></td>
<td class="tableblock halign-center valign-middle"><p class="tableblock"><a href="侧边栏API参考.html" class="xref page">侧边栏API参考</a></p></td>
<td class="tableblock halign-center valign-middle"><p class="tableblock"><a href="使用PetiteVue创建可复用组件.html" class="xref page">使用PetiteVue创建可复用组件</a></p></td>
<td class="tableblock halign-center valign-middle"><p class="tableblock"><a href="启用Steam客户端的DevTools.html" class="xref page">启用Steam客户端的DevTools</a></p></td>
</tr>
</tbody>
</table>
</div>
</div>
</article>
  </div>
</main>
</div>
<script id="site-script" src="../../../_/js/site.js" data-ui-root-path="../../../_"></script>
<script async src="../../../_/js/vendor/highlight.js"></script>

  </body>
</html>
